/**
 * 扰乱字符串
 *
 * 使用下面描述的算法可以扰乱字符串 s 得到字符串 t ：
 * 如果字符串的长度为 1 ，算法停止
 * 如果字符串的长度 > 1 ，执行下述步骤：
 * 在一个随机下标处将字符串分割成两个非空的子字符串。即，如果已知字符串 s ，则可以将其分成两个子字符串 x 和 y ，且满足 s = x + y 。
 * 随机 决定是要「交换两个子字符串」还是要「保持这两个子字符串的顺序不变」。即，在执行这一步骤之后，s 可能是 s = x + y 或者 s = y + x 。
 * 在 x 和 y 这两个子字符串上继续从步骤 1 开始递归执行此算法。
 * 给你两个 长度相等 的字符串 s1 和 s2，判断 s2 是否是 s1 的扰乱字符串。如果是，返回 true ；否则，返回 false 。
 *
 * 示例 1：
 * 输入：s1 = "great", s2 = "rgeat"
 * 输出：true
 * 解释：s1 上可能发生的一种情形是：
 * "great" --> "gr/eat"                 // 在一个随机下标处分割得到两个子字符串
 * "gr/eat" --> "gr/eat"                // 随机决定：「保持这两个子字符串的顺序不变」
 * "gr/eat" --> "g/r / e/at"            // 在子字符串上递归执行此算法。两个子字符串分别在随机下标处进行一轮分割
 * "g/r / e/at" --> "r/g / e/at"        // 随机决定：第一组「交换两个子字符串」，第二组「保持这两个子字符串的顺序不变」
 * "r/g / e/at" --> "r/g / e/ a/t"      // 继续递归执行此算法，将 "at" 分割得到 "a/t"
 * "r/g / e/ a/t" --> "r/g / e/ a/t"    // 随机决定：「保持这两个子字符串的顺序不变」
 * 算法终止，结果字符串和 s2 相同，都是 "rgeat"
 * 这是一种能够扰乱 s1 得到 s2 的情形，可以认为 s2 是 s1 的扰乱字符串，返回 true
 *
 * 示例 2：
 * 输入：s1 = "abcde", s2 = "caebd"
 * 输出：false
 *
 * 示例 3：
 * 输入：s1 = "a", s2 = "a"
 * 输出：true
 *
 * 提示：
 * s1.length == s2.length
 * 1 <= s1.length <= 30
 * s1 和 s2 由小写英文字母组成
 */

/**
 * 这题可以用递归和动态规划, 看到字符串问题想到动态规划, 分割想递归
 * 但是递归的时间复杂度太大了, 所以我们要用记忆化搜索来优化
 * 这题的递归函数的作用是: 就是以 i, j 开头, len 为长度的字符串
 * 是否可以满足题目需求, 函数主要处理一件事, 就是分割字符串, 分割好的字符串不换位,
 * 是否满足需求, 换位是否满足需求, 这里面在加上记忆化搜索就可以了
 * 时间复杂度 : O(n ^ 4)
 * 空间复杂度 : O(n ^ 3)
 */

public class Main {

    // 全局变量
    char[] s1;
    char[] s2;

    // 记忆化数组, 三种状态 :
    //                     -1 : 不可以
    //                      0 : 还未遍历
    //                      1 : 可以
    int[][][] dp;

    public boolean isScramble(String ss1, String ss2) {

        // 初始化
        int n = ss1.length();
        s1 = ss1.toCharArray();
        s2 = ss2.toCharArray();
        dp = new int[n][n][n + 1];

        // 从 0, 0 位置, 长度为 n
        return dfs (0, 0, n);
    }

    private boolean dfs (int i, int j, int len) {

        // 长度为 1, 看字符是否相等
        if (len == 1) {
            return s1[i] == s2[j];
        }

        // 记忆话搜索
        if (dp[i][j][len] == 1) {
            return true;
        } else if (dp[i][j][len] == -1) {
            return false;
        }

        // 分割字符
        for (int k = 1; k < len; k++) {

            // 不变换看是否满足
            if (dfs(i, j, k) && dfs(i + k, j + k, len - k)) {
                dp[i][j][len] = 1;
                return true;
            }

            // 变换看是否满足
            if (dfs(i, j + len - k, k) && dfs(i + k, j, len - k)) {
                dp[i][j][len] = 1;
                return true;
            }
        }

        // 不满足
        dp[i][j][len] = -1;
        return false;
    }
}
